﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.ServiceModel.DomainServices.Client;
using NetBay.Core.BusinessEntities;
using NetBay.Core.BusinessEntities.Versioning;
using NetBay.Core.Silverlight.Log;
using Rio.Framework.Localisation;
using Rio.Framework.Pages.Context;
using Rio.Framework.Pages.Events;
using Rio.Framework.WCFService;
using ImageRio = Rio.Framework.WCFService.RioWcfServices.ImageRio;
using WCFClient = Rio.Framework.WCFService.RioWcfServices;


namespace Rio.Framework.Pages.Manager
{
    /// <summary>
    /// Used this class to manage versioning operation on silverlight side
    /// </summary>
    public class VersioningManager : ManagerBase
    {

        #region Attributs

        private readonly string _msgAddVersion;
        private PostManyDocument _postManyDocument;
        private int _NbDocToLoad;
        private int _NbDocToLoad_OK;
        private int _NbDocToLoad_KO;
        private readonly string _msgTransferError;
        private int _countPostedDocument;
        private readonly string _msgIsImporting = string.Empty;
        private List<string> _filenameOnError;

        #endregion

        #region Properties

        /// <summary>
        /// Gets or sets the current new version.
        /// </summary>
        /// <value>
        /// The current new version.
        /// </value>
        private VersioningItem CurrentNewVersion { get; set; }
      
        #endregion

        #region Events

        public event VersioningEventHandler VersionCreated;
        /// <summary>
        /// Called when [version created].
        /// </summary>
        protected virtual void OnVersionCreated()
        {
            if (VersionCreated != null)
                VersionCreated(this, new VersioningEventArgs(CurrentNewVersion));
        }

        public event VersioningEventHandler CreationError;
        /// <summary>
        /// Called when [creation error].
        /// </summary>
        /// <param name="error">The error.</param>
        /// <param name="erroCode">The erro code.</param>
        protected virtual void OnCreationError(string error, int erroCode = 0)
        {
            //before raise event we must undocheckout the version
            if (CurrentNewVersion != null)
                UndoCheckoutVersion(CurrentNewVersion.VersionId);

            Logger.Write(error, Logger.Level.Error);

            if (CreationError != null)
                CreationError(this, new VersioningEventArgs(CurrentNewVersion, error, erroCode));
        }
        #endregion

        #region Constructors

        /// <summary>
        /// Initializes a new instance of the <see cref="VersioningManager"/> class.
        /// </summary>
        public VersioningManager()
        {
            _NbDocToLoad_OK = 0;
            _msgTransferError = string.Empty;
            //Used by postdocument
            SmartDispatcher.Initialize();
            _msgAddVersion = Resx.GetLabel(ResxFolderPage.ADD_ONE_VERSION);
        }

        #endregion

        #region Versionning

        /// <summary>
        /// Call AddVersionFromFile RIA query then call <see cref="OnAddVersionQueryCallback"/>
        /// </summary>
        /// <param name="documentId">The document identifier</param>
        /// <param name="newFile">The new file.</param>
        /// <param name="modelId">The model id.</param>
        /// <param name="isMajor">if set to <c>true</c> [is major].</param>
        public void AddVersionFromFile(long documentId, FileInfo newFile, long modelId, bool isMajor)
        {
            var query = GetDataContext.AddVersionFromFileQuery(documentId, newFile.Name, modelId, isMajor);
            GetDataContext.Load(query, OnAddVersionQueryCallback, newFile);
        }

        /// <summary>
        /// Call AddVersionFromOldVersion RIA query then call <see cref="WCFServiceClient_DuplicateDocument"/>,
        /// </summary>
        /// <param name="documentId">The document id.</param>
        /// <param name="oldVersion">The old version.</param>
        /// <param name="modelId">The model id.</param>
        /// <param name="isMajor">if set to <c>true</c> [is major].</param>
        public void AddVersionFromOldVersion(long documentId, VersioningItem oldVersion, long modelId, bool isMajor)
        {
            GetDataContext.AddVersionFromOldVersion(documentId, oldVersion, modelId, isMajor,
                delegate(InvokeOperation<IEnumerable<VersioningItem>> retr)
                {
                    if (retr != null && retr.Value != null && retr.Value.Count() > 0)
                    {
                        // here we have the old version and newversion => call wcf for moving document
                        CurrentNewVersion = retr.Value.First();
                        if (CurrentNewVersion != null && CurrentNewVersion.IsValid)
                        {
                            WCFClient.WCFServiceClient wcfServiceClient = ServiceManager.GetRioWCFServiceClient();
                            wcfServiceClient.Duplicate_DocumentCompleted += WCFServiceClient_DuplicateDocument;
                            wcfServiceClient.Duplicate_DocumentAsync(oldVersion.DocumentPath, CurrentNewVersion.DocumentPath, GlobalWebContext.ParameterStorage, CurrentNewVersion);
                            wcfServiceClient = null;
                        }
                        else
                        {
                            OnCreationError(" AddVersionFromOldVersion -> ERROR : " + Environment.NewLine + CurrentNewVersion.ErrorMessage);
                        }
                    }
                    else
                    {
                        OnCreationError("AddVersionFromOldVersion return no result!");
                    }
                }, null);
        }


        /// <summary>
        /// Call AddMajorVersionFromOldVersionQuery RIA query then call <see cref="WCFServiceClient_DuplicateDocument"/>
        /// </summary>
        /// <param name="documentId">The document id.</param>
        /// <param name="newFile">The new file.</param>
        /// <param name="fileName">Name of the file.</param>
        public void OverwriteVersion(long documentId, FileInfo newFile, string fileName = "")
        {
            string _fileName = fileName;
            if (newFile != null)
            {
                _fileName = newFile.Name;
            }

            GetDataContext.OverwriteVersion(documentId, _fileName,
                delegate(InvokeOperation<IEnumerable<VersioningItem>> retr)
                {
                    if (retr != null && retr.Value != null && retr.Value.Count() > 0)
                    {
                        // call wcf to post version and overwriting if exist
                        CurrentNewVersion = retr.Value.First();
                        if (CurrentNewVersion.IsValid)
                        {
                            PostVersion(CurrentNewVersion, newFile, true);
                        }        
                        else
                        {
                            OnCreationError(" OverwriteVersion -> ERROR : " + Environment.NewLine + CurrentNewVersion.ErrorMessage);
                        }
                    }
                    else
                    {
                        OnCreationError("OverwriteVersion return no result!");
                    }
                }, null);
        }

        /// <summary>
        /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
        /// </summary>
        public sealed override void Dispose()
        {
            base.Dispose();

            if (_postManyDocument != null)
            {
                _postManyDocument.Dispose();
                this._postManyDocument = null;
            }
        } 

        /// <summary>
        /// Duplicate Document version Completed
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="Rio.Framework.WCFService.RioWcfServices.Duplicate_DocumentCompletedEventArgs"/> instance containing the event data.</param>
        private void WCFServiceClient_DuplicateDocument(object sender, WCFClient.Duplicate_DocumentCompletedEventArgs e)
        {
            CurrentNewVersion = e.UserState as VersioningItem;

            if (e.Error != null)
            {
                //undo checkout on error
                OnCreationError("Error On DuplicateDocument -> " + e.Error.Message);
            }
            else
            {
                WCFClient.ReturnBoolean l_oReturnBoolean = e.Result;
                if (!l_oReturnBoolean.IsValid)
                {
                    //undo checkout on error
                    OnCreationError("Response not valid On DuplicateDocument -> " + l_oReturnBoolean.ErrorMessage);
                }
                else
                {
                    //Checkin version
                    if (CurrentNewVersion != null) 
                        CheckinVersion(CurrentNewVersion.VersionId);
                }
            }
        }

        /// <summary>
        /// Version was added to database, result contain the new version object with path and version identifier
        /// </summary>
        /// <param name="result"></param>
        private void OnAddVersionQueryCallback(LoadOperation<VersioningItem> result)
        {
            if (!result.HasError)
            {
                if (result.Entities != null && result.Entities.Count() > 0)
                {
                    CurrentNewVersion = result.Entities.FirstOrDefault();
                    if (CurrentNewVersion != null && CurrentNewVersion.IsValid)
                    {
                        PostVersion(result.Entities.FirstOrDefault(), (FileInfo)result.UserState, false);
                    }
                    else
                    {
                        if (CurrentNewVersion != null)
                            OnCreationError(" OnAddVersionQueryCallback -> ERROR : " + Environment.NewLine + CurrentNewVersion.ErrorMessage, CurrentNewVersion.ErrorCode);
                    }
                }
            }
            else
            {
                OnCreationError(" OnAddVersionQueryCallback -> EXCEPTION : " + Environment.NewLine + result.Error.Message);
            }
        }

        /// <summary>
        /// Version was added and checkouted, now we post the document 
        /// </summary>
        private void PostVersion(VersioningItem newVersion, FileInfo newFile, bool mustOverwrite)
        {
            //Post the document version
            if (newVersion != null && newFile != null)
            {
                CurrentNewVersion = newVersion;
                List<FileInfo> documentsToPost = new List<FileInfo> {newFile};
                PostDocuments(documentsToPost, newVersion.DocumentPath, mustOverwrite);
            }
        }

        /// <summary>
        /// Checkin the current new version
        /// </summary>
        /// <param name="versionIdentifier">The version identifier.</param>
        private void CheckinVersion(long versionIdentifier)
        {
            CheckinVersion(versionIdentifier,
                           delegate(InvokeOperation<ReturnBoolean> retr)
                               {
                                   if (retr != null && retr.Value != null && !retr.Value.Value)
                                   {
                                       OnCreationError(" CallCheckinVersion -> ERROR : " +
                                                       Environment.NewLine + retr.Value.ErrorMessage);
                                   }
                                   else
                                   {
                                       // raise event
                                       OnVersionCreated();
                                   }
                               });
        }

        /// <summary>
        /// Checkins the version.
        /// </summary>
        /// <param name="versionIdentifier">The version identifier.</param>
        /// <param name="callback">The callback.</param>
        public void CheckinVersion(long versionIdentifier, Action<InvokeOperation<ReturnBoolean>> callback)
        {
            GetDataContext.CheckinVersion(versionIdentifier, callback, null);
        }

        

        /// <summary>
        /// Undo checkout the current new version
        /// </summary>
        /// <param name="versionIdentifier">The version identifier.</param>
        private void UndoCheckoutVersion(long versionIdentifier)
        {
            UndoCheckoutVersion(versionIdentifier,
                                delegate(InvokeOperation<ReturnBoolean> retr)
                                    {
                                        if (retr != null && retr.Value != null && !retr.Value.Value)
                                        {
                                            throw new Exception(" CallUndoCheckoutVersion -> ERROR : " +
                                                                Environment.NewLine + retr.Value.ErrorMessage);
                                        }
                                        OnVersionCreated();
                                    });

        }

        /// <summary>
        /// Undoes the checkout version.
        /// </summary>
        /// <param name="versionIdentifier">The version identifier.</param>
        /// <param name="callback">The callback.</param>
        public void UndoCheckoutVersion(long versionIdentifier, Action<InvokeOperation<ReturnBoolean>> callback)
        {
            GetDataContext.UndoCheckout(versionIdentifier, callback, null);
        }

        #endregion

        #region PostDocuments

        /// <summary>
        /// Post all documents to the remote destination path
        /// </summary>
        /// <param name="files">The files.</param>
        /// <param name="destinationPath">The destination path.</param>
        /// <param name="overwrite">if set to <c>true</c> [overwrite].</param>
        private void PostDocuments(IEnumerable<FileInfo> files, string destinationPath, bool overwrite)
        {
            try
            {
                _filenameOnError = new List<string>();
                _NbDocToLoad = 0;
                _NbDocToLoad_KO = 0;
                _NbDocToLoad_OK = 0;

                SplashScreenManager.SplashScreen.Message = _msgIsImporting;

                if (files != null && files.Count() > 0)
                {
                    //On appel le service pour savoir si les fichiers exstent deja ou pas.
                    _countPostedDocument = 0;
                    _NbDocToLoad = files.Count();

                    _postManyDocument = new PostManyDocument(GlobalWebContext.CurrentUser.UserId, files, 1024 * 1024 * 2, destinationPath, GlobalWebContext.ParameterStorage, overwrite);
                    _postManyDocument.EndOfPost += PostManyDocumentEndOfPost;
                    _postManyDocument.PostError += PostManyDocumentPostError;
                    _postManyDocument.Do(OnBeforePostDocument, OnPostManyDocumentProgress);
                }
            }
            catch
            {
                if (_NbDocToLoad_OK + _NbDocToLoad_KO == _NbDocToLoad)
                {
                    OnCreationError("PostDocuments error!");
                }
            }
        }

        /// <summary>
        /// Post Error - Undo Checkout version
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void PostManyDocumentPostError(object sender, EventArgs e)
        {
            // undo checkout version
            OnCreationError("Une erreur a causée l'interruption de l'import de fichier");

            _postManyDocument.EndOfPost -= PostManyDocumentEndOfPost;
            _postManyDocument.PostError -= PostManyDocumentPostError;
        }

        /// <summary>
        /// Post Done - Check if error
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void PostManyDocumentEndOfPost(object sender, EventArgs e)
        {
            if (_NbDocToLoad_KO != 0)
            {
                // undo checkout version
                OnCreationError(string.Format(_msgTransferError, _NbDocToLoad_KO));
            }
            else
            {
                // checkin version
                CheckinVersion(CurrentNewVersion.VersionId);
            }

            _postManyDocument.EndOfPost -= PostManyDocumentEndOfPost;
            _postManyDocument.PostError -= PostManyDocumentPostError;
        }

        /// <summary>
        /// Called when [before post document].
        /// </summary>
        /// <param name="postedDocumentName">Name of the posted document.</param>
        private void OnBeforePostDocument(string postedDocumentName)
        {
            SplashScreenManager.SplashScreen.Message = _msgAddVersion;
            SplashScreenManager.SplashScreen.MessageDetail = postedDocumentName;
        }

        /// <summary>
        /// Post progression
        /// </summary>
        /// <param name="percentageOfDone">The p_i percentage of done.</param>
        /// <param name="errorMessage">The P_SZ error message.</param>
        /// <param name="thumbNail">The p_o thumb nail.</param>
        private void OnPostManyDocumentProgress(int percentageOfDone, string errorMessage, ImageRio thumbNail)
        {
            try
            {
                if (percentageOfDone >= 0)
                {
                    if (!string.IsNullOrEmpty(errorMessage))
                    {
                        throw new Exception(errorMessage);
                    }

                    if (percentageOfDone == 100)
                    {
                        // +1 document posted
                        _countPostedDocument++;
                        _NbDocToLoad_OK++;
                    }
                }
                else
                {
                    if (_filenameOnError != null && !string.IsNullOrEmpty(errorMessage) && !_filenameOnError.Contains(errorMessage))
                    {
                        _NbDocToLoad_KO++;
                        _filenameOnError.Add(errorMessage);
                    }
                }
            }
            catch (Exception ex)
            {
                Logger.Write(" OnPostManyDocumentProgress -> EXCEPTION : " + Environment.NewLine + ex.Message, Logger.Level.Error);
                _NbDocToLoad_KO++;
                OnCreationError("Importation de la nouvelle version impossible, le transfert a échoué!");
            }
        }

        #endregion
    }
}
